﻿﻿<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>在线文件盖章_印好章</title>
    <meta name="keywords" content="在线文件盖章，文件盖章，图片盖章">
    <meta name="description" content="印好章在线文件盖章工具，打开 pdf 或者图片文件，打开印章图片，将印章盖到文件上。支持设置印章图片的大小、旋转角、透明度等参数，支持设置盖章的文件页数范围。">

    <link rel="stylesheet" href="css/index.css">
    <link rel="stylesheet" href="css/app.css">
    
    <!-- jQuery -->
    <link rel="stylesheet" href="css/jquery-ui.css">
    <script src="js/jquery-1.9.1.js"></script>
    <script src="js/jquery-ui.js"></script>
    
    <!-- PDF.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.min.js"></script>
    
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        [v-cloak] {
            display: none;
        }
        
        .main {
            width: 100%;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            padding: 40px 0 100px 0;
        }
        .title {
            line-height: 2;
            font-size: 34px;
            font-weight: bold;
        }
        .content {
            max-width: 1200px;
            min-width: 400px;
            height: auto;
            box-shadow: 0 0 10px lightgray;
            display: flex;
            flex-wrap: wrap;
            position: relative;
        }
        
        .seal {
            left: 62%;
            top: 46%;
            position: absolute;
            opacity: 0.8;
            cursor: move;
        }
        .setbox {
            width: 50%;
            height: auto;
            display: flex;
            flex-direction: column;
            white-space: nowrap;
        }
        /* 新增加载样式 */
        .loading-mask {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(255,255,255,0.8);
            z-index: 9999;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 24px;
            color: #409EFF;
        }
        /* 新增拖拽蒙版样式 */
        .drag-mask {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(255, 255, 255, 0.8);
            z-index: 9998;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 24px;
            color: #409EFF;
            border: 2px dashed #409EFF;
            pointer-events: none;
        }
        .drag-hint {
            padding: 20px;
            background: white;
            border-radius: 5px;
            box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
        }
    </style>
    
    <script>
        $(function() {
            // 初始化印章图片的拖拽功能
            $(".seal").draggable({
                containment: "parent", // 限制拖拽范围在父元素内
                drag: function(event, ui) {
                    // 实时更新印章的坐标
                    var x = Math.round(ui.position.left);
                    var y = Math.round(ui.position.top);
                    $(".sealXY").text('X：' + x + '，Y：' + y);
                },
            });
        });
    </script>
</head>
<body>
    <div id="app" v-cloak>
        <!-- 加载状态提示 -->
        <div class="loading-mask" v-if="isLoading">
            <div class="loader">加载中...</div>
        </div>
        <!-- 新增拖拽蒙版 -->
        <div class="drag-mask" v-if="isDragging">
            <div class="drag-hint">松开鼠标上传文档</div>
        </div>
        <div class="main">
            <div class="setbox">
                <div class="w-full flex justify-evenly text-sm flex-wrap">
                    <!-- 文档图片上传 -->
                    <div class="flex items-center">
                        <span>文档图片（ctrl+v可读取剪贴板）：</span>
                        <label>
                            <div class="flex px-6 py-1 text-gray-500 rounded items-center cursor-pointer" style="border: 1px solid lightgray;">
                                <i class="el-icon-upload2 mr-2"></i>
                                上传
                            </div>
                            <input type="file" id="file" hidden="hidden" accept="image/*, application/pdf" @change="uploadDoc">
                        </label>
						<!-- 新增PDF页面选择 -->
						<div class="flex text-sm items-center" v-if="isPDF">
							<span>选择页面：</span>
							<el-select v-model="currentPage" @change="changePage" style="width: 100px;">
								<el-option
									v-for="page in pdfPages"
									:key="page"
									:label="'第' + page + '页'"
									:value="page">
								</el-option>
							</el-select>
						</div>
                    </div>
                    <!-- 印章图片上传 -->
                    <div class="flex items-center">
                        <span>印章图片：</span>
                        <label>
                            <div class="flex px-6 py-1 text-gray-500 rounded flex items-center cursor-pointer" style="border: 1px solid lightgray;">
                                <i class="el-icon-upload2 mr-2"></i>
                                上传
                            </div>
                            <input type="file" id="file" hidden="hidden" accept="image/*" @change="uploadSeal">
                        </label>
                    </div>
                </div>
                <div class="w-full flex justify-evenly flex-wrap">
                    <span class="flex items-center text-blue-600">印章设置</span>
                    <!-- 印章大小设置 -->
                    <div class="flex text-sm items-center">
                        <span>大小：</span>
                        <el-slider v-model="sealSize" :min="1" :max="100" style="width: 140px;"></el-slider>
                    </div>
                    <!-- 印章透明度设置 -->
                    <div class="flex text-sm items-center">
                        <span>透明度：</span>
                        <el-slider v-model="sealOpacity" :min="20" :max="100" style="width: 140px;"></el-slider>
                    </div>
                    <!-- 印章旋转角度设置 -->
                    <div class="flex text-sm items-center">
                        <span class="dataTitle">旋转°：</span>
                        <el-slider v-model="sealRotate" :min="0" :max="360" style="width: 140px;"></el-slider>
                    </div>
                </div>
                <!-- 文件列表 -->
                <div class="flex" v-if="files.length > 0" style="flex-wrap: wrap;">
                    <div class="flex mt-1 px-6 py-1 text-gray-500 rounded flex items-center cursor-pointer ml-1" v-for="(item, index) in files" :key="index" style="border: 1px solid lightgray;" @click="tab(item)">{{item}}</div>
                </div>
                <!-- 无文件时的提示 -->
                <div style="text-align:center;" v-else>
                    <a href="/admin">去登录</a>
                </div>
            </div>
            <!-- 文档和印章展示区域 -->
            <div class="content mt-4">
                <img :src="docImgSrc">
                <img class="seal" :src="sealImgSrc" :width="sealSize*10" :style="{opacity: sealOpacity / 100, transform: 'rotate(' + sealRotate + 'deg)'}">
            </div>
            <!-- 下载按钮 -->
            <el-button type="success" class="mt-6" @click="download">下载图片</el-button>
            <a :href="downImgSrc" download class="downImg hidden"></a>
        </div>
    </div>
    
    <!-- Vue.js -->
    <script src="js/vue.js"></script>
    <!-- Axios -->
    <script src="js/axios.min.js"></script>
    <!-- 主逻辑 -->
    <script src="js/index.js"></script>
    <!-- HTML转Canvas工具 -->
    <script src="js/html2canvas.js?ver=1.0.0"></script>
    
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    docImgSrc: 'https://static-o.oss-cn-shenzhen.aliyuncs.com/sukoutu/images/affixMode.png', // 默认文档图片
                    sealImgSrc: 'images/%E5%B9%BF%E5%B7%9E%E4%BD%91%E6%99%9F', // 默认印章图片
                    sealSize: 25, // 印章大小
                    sealOpacity: 50, // 印章透明度
                    sealRotate: -20, // 印章旋转角度
                    downImgSrc: '', // 下载图片的URL
                    files: [], // 文件列表
                    // 新增PDF相关数据
                    isPDF: false,
                    pdfDoc: null,
                    pdfPages: 1,
                    currentPage: 1,
                    isLoading: false, // 加载状态
					// 新增拖拽状态
                    isDragging: false,
                };
            },
            methods: {
			
				// 文件类型验证方法
				isValidFileType(file) {
					const acceptedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
					return acceptedTypes.includes(file.type);
				},
			
                /**
                 * 上传文档图片
                 * @param {Event} event - 文件上传事件
                 */
				 
                async uploadDoc(event) {
                    this.isLoading = true;
                    const file = event.target.files[0];
                    event.target.value = '';
					that = this;
                    if (file) {
                        if (file.type === 'application/pdf') {
                            this.isPDF = true;
                            await this.loadPDF(file);
                        } else {
                            this.isPDF = false;
                            // 原有图片处理逻辑...
                            // 如果是图片文件，直接读取并显示
                            const reader = new FileReader();
                            reader.onload = (e) => {
                                that.docImgSrc = e.target.result;
                            };
                            reader.readAsDataURL(file);
                        }
                    }
                    this.isLoading = false;
                },
				
                async loadPDF(file) {
                    const reader = new FileReader();
                    reader.onload = async (e) => {
                        const typedarray = new Uint8Array(e.target.result);
                        this.pdfDoc = await pdfjsLib.getDocument(typedarray).promise;
                        this.pdfPages = this.pdfDoc.numPages;
                        this.currentPage = 1;
                        await this.renderPage(this.currentPage);
                    };
                    reader.readAsArrayBuffer(file);
                },
				
				
                /**
                 * 将PDF文件渲染为图片
                 * @param {File} file - PDF文件
                 */
                async renderPage(pageNumber) {
                    this.isLoading = true;
                    const page = await this.pdfDoc.getPage(pageNumber);
                    const scale = 1.5;
                    const viewport = page.getViewport({ scale });
                    
                    const canvas = document.createElement('canvas');
                    const context = canvas.getContext('2d');
                    canvas.height = viewport.height;
                    canvas.width = viewport.width;

                    await page.render({
                        canvasContext: context,
                        viewport: viewport
                    }).promise;

                    this.docImgSrc = canvas.toDataURL('image/jpeg', 1.0);
                    this.isLoading = false;
                },
                changePage() {
                    this.renderPage(this.currentPage);
                },
                /**
                 * 上传印章图片
                 * @param {Event} event - 文件上传事件
                 */
                uploadSeal(event) {
					this.isLoading = true;
                    let that = this;
                    const file = event.target.files[0];
                    event.target.value = ''; // 清空文件选择框
                    if (file) {
                        const reader = new FileReader();
                        reader.onload = (e) => {
                            that.sealImgSrc = e.target.result;
                        };
                        reader.readAsDataURL(file);
                    }
					this.isLoading = false;
                },
                /**
                 * 下载图片
                 */
                download() {
                    let that = this;
                    // 使用html2canvas将指定DOM元素转为Canvas
                    html2canvas(document.querySelector('.content'), { useCORS: true }).then(canvas => {
                        // 将Canvas转为图片URL
                        const dataUrl = canvas.toDataURL('image/jpeg', 1);
                        that.downImgSrc = dataUrl;
                        // 触发下载
                        setTimeout(function() {
                            document.querySelector('.downImg').click();
                        }, 500);
                    });
                },
                /**
                 * 切换印章图片
                 * @param {string} obj - 印章文件名
                 */
                tab(obj) {
                    let that = this;
                    that.sealImgSrc = 'images/' + obj;
                },
                /**
                 * 加载文件列表
                 */
                loadData() {
                    let that = this;
                    axios.get('/admin/index/get_dir')
                        .then(response => {
                            if (typeof response.data === 'object' && response.data !== null) {
                                that.files = response.data;
                            }
                        })
                        .catch(error => {
                            console.error('加载文件列表失败:', error);
                        });
                }
            },
            mounted() {
                console.log('页面加载完成');
                // 初始化时加载文件列表
                this.loadData();
                
                // 监听粘贴事件
                document.addEventListener('paste', (e) => {
                    const items = e.clipboardData.items;
                    for (let i = 0; i < items.length; i++) {
                        if (items[i].type.startsWith('image/')) {
                            e.preventDefault();
                            const blob = items[i].getAsFile();
                            const file = new File([blob], 'pasted-image.png', {type: items[i].type});
                            const event = { target: { files: [file] } };
                            this.uploadDoc(event);
                            break;
                        }
                    }
                });
                
    // 拖拽事件处理
				const handleDragEnter = (e) => {
					if (e.dataTransfer.types.includes('Files')) {
						this.isDragging = true;
						e.preventDefault();
					}
				};

				const handleDragOver = (e) => {
					if (e.dataTransfer.types.includes('Files')) {
						e.preventDefault();
					}
				};

				const handleDragLeave = (e) => {
					if (this.isDragging) {
						this.isDragging = false;
					}
				};

				const handleDrop = (e) => {
					this.isDragging = false;
					e.preventDefault();

					const files = e.dataTransfer.files;
					if (files.length > 0) {
						const file = files[0];
						if (this.isValidFileType(file)) {
							const event = { target: { files: [file] } };
							this.uploadDoc(event);
						} else {
							alert('请上传PDF或图片文件！');
						}
					}
				};

				document.addEventListener('dragenter', handleDragEnter);
				document.addEventListener('dragover', handleDragOver);
				document.addEventListener('dragleave', handleDragLeave);
				document.addEventListener('drop', handleDrop);

				// 组件销毁时移除监听
				this.$once('hook:beforeDestroy', () => {
					document.removeEventListener('dragenter', handleDragEnter);
					document.removeEventListener('dragover', handleDragOver);
					document.removeEventListener('dragleave', handleDragLeave);
					document.removeEventListener('drop', handleDrop);
				});
            },
        });
    </script>
</body>
</html>